/*
 *	Copyright (c) 2010, tn
 *	All rights reserved.
 *
 *	Redistribution and use in source and binary forms, with or without
 *	modification, are permitted provided that the following conditions are met:
 *	    * Redistributions of source code must retain the above copyright
 *	      notice, this list of conditions and the following disclaimer.
 *	    * Redistributions in binary form must reproduce the above copyright
 *	      notice, this list of conditions and the following disclaimer in the
 *	      documentation and/or other materials provided with the distribution.
 *	    * Neither the name of the <organization> nor the
 *	      names of its contributors may be used to endorse or promote products
 *	      derived from this software without specific prior written permission.
 *
 *	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *	DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 *	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef MCUNIT_H_
#define MCUNIT_H_

#include <stdint.h>
#include <stddef.h>

#ifndef MCUNIT_PRINTF
#define MCUNIT_PRINTF printf
#endif

/**
 *	Declare your test functions using this macro
 */
#define MCUNIT_TEST(name)	int name(mcunit_test_s *_test)

/**
 * 	Default assert function. Nothing fancy here.
 */
#define MCUNIT_ASSERT( x )	if( !(x) ) { printf("Failed: %s:%d: %s\n", _test->name, __LINE__, #x); return 1; }

/** Use this to set suite runner msg indent. Default is 0. */
extern const char *mcunit_suite_indent;

/** Use this to set case runner msg indent. Default is \t. */
extern const char *mcunit_case_indent;

/** Use this to set case runner msg indent. Default is \t\t. */
extern const char *mcunit_test_indent;

enum mcunit_codes {
	mcunit_test_ok =  0,
	mcunit_no_test = -1
};

/**
 * 	Individual test function. Message in msg will be printf'ed on report
 * 	generation, so you can set it inside your test to tell something about
 * 	what happened (why it failed). Default should be set to NULL, so no that
 * 	no message will be printf'ed. If pointer to the function is NULL,
 * 	test will not be run. This indicates end-of-list and case runner
 * 	will stop on such test. NULLs. Always keep NULLs in mind.
 */
typedef struct mcunit_test {
	int (*test)(struct mcunit_test *_test);	/*!< Pointer to test function           */
	int	result;								/*!< Test result, 0 is success          */
	const char *name;						/*!< Optional test name                 */
	const char *msg;						/*!< Hook-up any message you want here. */
} mcunit_test_s;

/**
 * 	Just a helper typedef.
 */
typedef int (*mcunit_test_fp)(struct mcunit_test *_test);

/**
 * 	Aggregates tests into test case. You don't need to initializa passed
 * 	and runs, as they will be set correctly by the runner. Focus on *name
 * 	and tests. List should be terminated with an empty test, ie. test
 *	with test function pointer set to NULL! Tests are executed from
 *	tests[0] to tests[n], so put your setup function first and tear down
 *	last (but before NULL-terminator!) Remember about NULLs. NULLs are important.
 */
typedef struct mcunit_case {
	const char   *name;		/*!< It's a good idea to set some friendly name */
	int           passed;	/*!< Counts passed tests                        */
	int           runs;		/*!< Counts how many tests are run              */
	mcunit_test_s tests[];	/*!< Terminate list with NULL-ed test function! */
} mcunit_case_s;

/**
 * 	Aggregates test cases into suite. Think about setting some friendly name,
 * 	don't bother with passed and runs as they are reset by the runner. Cases list
 * 	should be NULL-terminated! Cases are run from cases[0] to cases[n]
 *  (assuming cases[n+1] == NULL). Yes, NULLs are important. Again.
 */
typedef struct mcunit_suite {
	const char *name;		/*!< Suite name, used by report generator           */
	int passed;				/*!< Passed cases counter                           */
	int runs;				/*!< Run cases counter                              */
	mcunit_case_s *cases[]; /*!< NULL-terminated list of pointers to test cases */
} mcunit_suite_s;

/**
 * 	Runs single test.
 * 	\param			Pointer to the test structure containing test function pointer, result code, etc.
 * 	\retvalue	0	Success, test passed
 * 	\retvalue	>0	Test failed. Result will be kept in test structure for repor generator.
 * 	\retvalue	-1	No test function set, this means end-of-list for case runner.
 */
int mcunit_test_run(mcunit_test_s *test);

/**
 * 	Runs test case.
 * 	\param	Pointer to the test case structure containing list of individial tests,	result code, etc.
 * 	\return	Number of failed tests. 0 means all tests passed.
 */
int mcunit_case_run(mcunit_case_s *test_case);

/**
 * 	Runs test suite.
 * 	\param	Pointer to the test suite structure containing list of test cases, result code, etc.
 * 	\return	Number of failed test cases. 0 means all test cases passed.
 */
int mcunit_suite_run(mcunit_suite_s *test_suite);

/**
 * 	Print test status.
 * 	\param	test 	Pointer to the test structure containing test results.
 */
void mcunit_test_report(mcunit_test_s *test);

/**
 * 	Print test case results. If depth == 0, only brief results are printf'ed.
 * 	\param	test_case 	Pointer to the test structure containing test results.
 * 	\param	depth 		If >0, print individual test results too.
 */
void mcunit_case_report(mcunit_case_s *test_case, int depth);

/**
 * 	Print test case results. If depth == 0, only brief results are printf'ed.
 * 	\param	test_case 	Pointer to the test structure containing test results.
 * 	\param	depth 		If 1, print individual test cases results too. If >=2, print individial tests too (full report)
 */
void mcunit_suite_report(mcunit_suite_s *test_suite, int depth);

#endif /* MCUNIT_H_ */
